using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using HslCommunication.Core;
using HslCommunication.Core.Net;
using Newtonsoft.Json.Linq;

namespace HslCommunication.Enthernet
{
	/// <summary>
	/// 终极文件管理服务器，实现所有的文件分类管理，读写分离，不支持直接访问文件名
	/// </summary>
	/// <remarks>
	/// 本文件的服务器支持存储文件携带的额外信息，文件名被映射成了新的名称，无法在服务器直接查看文件信息。
	/// </remarks>
	/// <example>
	/// 以下的示例来自Demo项目，创建了一个简单的服务器对象。
	/// <code lang="cs" source="TestProject\FileNetServer\FormFileServer.cs" region="Ultimate Server" title="UltimateFileServer示例" />
	/// </example>
	public class UltimateFileServer : NetworkFileServerBase
	{
		/// <summary>
		/// 所有文件组操作的词典锁
		/// </summary>
		internal Dictionary<string, GroupFileContainer> m_dictionary_group_marks = new Dictionary<string, GroupFileContainer>();

		/// <summary>
		/// 词典的锁
		/// </summary>
		private SimpleHybirdLock hybirdLock = new SimpleHybirdLock();

		/// <summary>
		/// 实例化一个对象
		/// </summary>
		public UltimateFileServer()
		{
		}

		/// <summary>
		/// 获取当前目录的读写锁，如果没有会自动创建
		/// </summary>
		/// <param name="filePath">相对路径名</param>
		/// <returns>读写锁</returns>
		public GroupFileContainer GetGroupFromFilePath(string filePath)
		{
			GroupFileContainer groupFileContainer = null;
			hybirdLock.Enter();
			if (m_dictionary_group_marks.ContainsKey(filePath))
			{
				groupFileContainer = m_dictionary_group_marks[filePath];
			}
			else
			{
				groupFileContainer = new GroupFileContainer(base.LogNet, filePath);
				m_dictionary_group_marks.Add(filePath, groupFileContainer);
			}
			hybirdLock.Leave();
			return groupFileContainer;
		}

		/// <summary>
		/// 从套接字接收文件并保存，更新文件列表
		/// </summary>
		/// <param name="socket">套接字</param>
		/// <param name="savename">保存的文件名</param>
		/// <returns>是否成功的结果对象</returns>
		private OperateResult<FileBaseInfo> ReceiveFileFromSocketAndUpdateGroup(Socket socket, string savename)
		{
			FileInfo fileInfo = new FileInfo(savename);
			string text = CreateRandomFileName();
			string text2 = Path.Combine(fileInfo.DirectoryName, text);
			OperateResult<FileBaseInfo> operateResult = ReceiveFileFromSocket(socket, text2, null);
			if (!operateResult.IsSuccess)
			{
				DeleteFileByName(text2);
				return operateResult;
			}
			GroupFileContainer groupFromFilePath = GetGroupFromFilePath(fileInfo.DirectoryName);
			string fileName = groupFromFilePath.UpdateFileMappingName(fileInfo.Name, operateResult.Content.Size, text, operateResult.Content.Upload, operateResult.Content.Tag);
			DeleteExsistingFile(fileInfo.DirectoryName, fileName);
			OperateResult operateResult2 = SendStringAndCheckReceive(socket, 1, StringResources.Language.SuccessText);
			if (!operateResult2.IsSuccess)
			{
				return OperateResult.CreateFailedResult<FileBaseInfo>(operateResult2);
			}
			return OperateResult.CreateSuccessResult(operateResult.Content);
		}

		/// <summary>
		/// 从套接字接收文件并保存，更新文件列表
		/// </summary>
		/// <param name="socket">套接字</param>
		/// <param name="savename">保存的文件名</param>
		/// <returns>是否成功的结果对象</returns>
		private async Task<OperateResult<FileBaseInfo>> ReceiveFileFromSocketAndUpdateGroupAsync(Socket socket, string savename)
		{
			FileInfo info = new FileInfo(savename);
			string guidName = CreateRandomFileName();
			string fileName = Path.Combine(info.DirectoryName, guidName);
			OperateResult<FileBaseInfo> receive = await ReceiveFileFromSocketAsync(socket, fileName, null);
			if (!receive.IsSuccess)
			{
				DeleteFileByName(fileName);
				return receive;
			}
			GroupFileContainer fileManagment = GetGroupFromFilePath(info.DirectoryName);
			DeleteExsistingFile(fileName: fileManagment.UpdateFileMappingName(info.Name, receive.Content.Size, guidName, receive.Content.Upload, receive.Content.Tag), path: info.DirectoryName);
			OperateResult sendBack = await SendStringAndCheckReceiveAsync(socket, 1, StringResources.Language.SuccessText);
			if (!sendBack.IsSuccess)
			{
				return OperateResult.CreateFailedResult<FileBaseInfo>(sendBack);
			}
			return OperateResult.CreateSuccessResult(receive.Content);
		}

		/// <summary>
		/// 根据文件的显示名称转化为真实存储的名称，例如 123.txt 获取到在文件服务器里映射的文件名称，例如返回 b35a11ec533147ca80c7f7d1713f015b7909
		/// </summary>
		/// <param name="factory">第一大类</param>
		/// <param name="group">第二大类</param>
		/// <param name="id">第三大类</param>
		/// <param name="fileName">文件显示名称</param>
		/// <returns>是否成功的结果对象</returns>
		private string TransformFactFileName(string factory, string group, string id, string fileName)
		{
			string filePath = ReturnAbsoluteFilePath(factory, group, id);
			GroupFileContainer groupFromFilePath = GetGroupFromFilePath(filePath);
			return groupFromFilePath.GetCurrentFileMappingName(fileName);
		}

		/// <summary>
		/// 删除已经存在的文件信息，文件的名称需要是guid名称，例如 b35a11ec533147ca80c7f7d1713f015b7909
		/// </summary>
		/// <param name="path">文件的路径</param>
		/// <param name="fileName">文件的guid名称，例如 b35a11ec533147ca80c7f7d1713f015b7909</param>
		private void DeleteExsistingFile(string path, string fileName)
		{
			DeleteExsistingFile(path, new List<string>
			{
				fileName
			});
		}

		/// <summary>
		/// 删除已经存在的文件信息，文件的名称需要是guid名称，例如 b35a11ec533147ca80c7f7d1713f015b7909
		/// </summary>
		/// <param name="path">文件的路径</param>
		/// <param name="fileNames">文件的guid名称，例如 b35a11ec533147ca80c7f7d1713f015b7909</param>
		private void DeleteExsistingFile(string path, List<string> fileNames)
		{
			foreach (string fileName in fileNames)
			{
				if (string.IsNullOrEmpty(fileName))
				{
					continue;
				}
				string fileUltimatePath = Path.Combine(path, fileName);
				FileMarkId fileMarksFromDictionaryWithFileName = GetFileMarksFromDictionaryWithFileName(fileName);
				fileMarksFromDictionaryWithFileName.AddOperation(delegate
				{
					if (!DeleteFileByName(fileUltimatePath))
					{
						base.LogNet?.WriteInfo(ToString(), StringResources.Language.FileDeleteFailed + fileUltimatePath);
					}
					else
					{
						base.LogNet?.WriteInfo(ToString(), StringResources.Language.FileDeleteSuccess + fileUltimatePath);
					}
				});
			}
		}

		/// <inheritdoc />
		protected override async void ThreadPoolLogin(Socket socket, IPEndPoint endPoint)
		{
			string IpAddress = endPoint.Address.ToString();
			OperateResult<FileGroupInfo> infoResult = await ReceiveInformationHeadAsync(socket);
			if (!infoResult.IsSuccess)
			{
				return;
			}
			int customer = infoResult.Content.Command;
			string Factory = infoResult.Content.Factory;
			string Group = infoResult.Content.Group;
			string Identify = infoResult.Content.Identify;
			string fileName = infoResult.Content.FileName;
			string relativeName2 = GetRelativeFileName(Factory, Group, Identify, fileName);
			switch (customer)
			{
			case 2001:
			{
				string guidName = TransformFactFileName(Factory, Group, Identify, fileName);
				FileMarkId fileMarkId = GetFileMarksFromDictionaryWithFileName(guidName);
				fileMarkId.EnterReadOperator();
				OperateResult send = await SendFileAndCheckReceiveAsync(socket, ReturnAbsoluteFileName(Factory, Group, Identify, guidName), fileName, "", "");
				if (!send.IsSuccess)
				{
					fileMarkId.LeaveReadOperator();
					base.LogNet?.WriteError(ToString(), StringResources.Language.FileDownloadFailed + " : " + send.Message + " :" + relativeName2 + " ip:" + IpAddress);
				}
				else
				{
					base.LogNet?.WriteInfo(ToString(), StringResources.Language.FileDownloadSuccess + ":" + relativeName2);
					fileMarkId.LeaveReadOperator();
					socket?.Close();
				}
				break;
			}
			case 2002:
			{
				string fullFileName4 = ReturnAbsoluteFileName(Factory, Group, Identify, fileName);
				CheckFolderAndCreate();
				FileInfo info4 = new FileInfo(fullFileName4);
				try
				{
					if (!Directory.Exists(info4.DirectoryName))
					{
						Directory.CreateDirectory(info4.DirectoryName);
					}
				}
				catch (Exception ex2)
				{
					Exception ex = ex2;
					base.LogNet?.WriteException(ToString(), StringResources.Language.FilePathCreateFailed + fullFileName4, ex);
					socket?.Close();
					return;
				}
				OperateResult<FileBaseInfo> receive = await ReceiveFileFromSocketAndUpdateGroupAsync(socket, fullFileName4);
				if (receive.IsSuccess)
				{
					socket?.Close();
					OnFileUpload(new FileServerInfo
					{
						ActualFileFullName = fullFileName4,
						Name = receive.Content.Name,
						Size = receive.Content.Size,
						Tag = receive.Content.Tag,
						Upload = receive.Content.Upload
					});
					base.LogNet?.WriteInfo(ToString(), StringResources.Language.FileUploadSuccess + ":" + relativeName2);
				}
				else
				{
					base.LogNet?.WriteInfo(ToString(), StringResources.Language.FileUploadFailed + ":" + relativeName2);
				}
				break;
			}
			case 2003:
			{
				string fullFileName3 = ReturnAbsoluteFileName(Factory, Group, Identify, fileName);
				FileInfo info3 = new FileInfo(fullFileName3);
				DeleteExsistingFile(fileName: GetGroupFromFilePath(info3.DirectoryName).DeleteFile(info3.Name), path: info3.DirectoryName);
				if ((await SendStringAndCheckReceiveAsync(socket, 1, "success")).IsSuccess)
				{
					socket?.Close();
				}
				base.LogNet?.WriteInfo(ToString(), StringResources.Language.FileDeleteSuccess + ":" + relativeName2);
				break;
			}
			case 2011:
			{
				string[] fileNames = infoResult.Content.FileNames;
				foreach (string item in fileNames)
				{
					string fullFileName2 = ReturnAbsoluteFileName(Factory, Group, Identify, item);
					FileInfo info2 = new FileInfo(fullFileName2);
					DeleteExsistingFile(fileName: GetGroupFromFilePath(info2.DirectoryName).DeleteFile(info2.Name), path: info2.DirectoryName);
					relativeName2 = GetRelativeFileName(Factory, Group, Identify, fileName);
					base.LogNet?.WriteInfo(ToString(), StringResources.Language.FileDeleteSuccess + ":" + relativeName2);
				}
				if ((await SendStringAndCheckReceiveAsync(socket, 1, "success")).IsSuccess)
				{
					socket?.Close();
				}
				break;
			}
			case 2012:
			{
				string fullFileName = ReturnAbsoluteFileName(Factory, Group, Identify, "123.txt");
				FileInfo info = new FileInfo(fullFileName);
				DeleteExsistingFile(fileNames: GetGroupFromFilePath(info.DirectoryName).ClearAllFiles(), path: info.DirectoryName);
				if ((await SendStringAndCheckReceiveAsync(socket, 1, "success")).IsSuccess)
				{
					socket?.Close();
				}
				base.LogNet?.WriteInfo(ToString(), "FolderDelete : " + relativeName2);
				break;
			}
			case 2007:
			{
				GroupFileContainer fileManagment = GetGroupFromFilePath(ReturnAbsoluteFilePath(Factory, Group, Identify));
				if ((await SendStringAndCheckReceiveAsync(socket, 2007, fileManagment.JsonArrayContent)).IsSuccess)
				{
					socket?.Close();
				}
				break;
			}
			case 2008:
			{
				List<string> folders = new List<string>();
				string[] directories = GetDirectories(Factory, Group, Identify);
				foreach (string i in directories)
				{
					DirectoryInfo directory = new DirectoryInfo(i);
					folders.Add(directory.Name);
				}
				JArray jArray = JArray.FromObject((object)folders.ToArray());
				if ((await SendStringAndCheckReceiveAsync(socket, 2007, ((object)jArray).ToString())).IsSuccess)
				{
					socket?.Close();
				}
				break;
			}
			default:
				socket?.Close();
				break;
			}
		}

		/// <inheritdoc />
		public override string ToString()
		{
			return $"UltimateFileServer[{base.Port}]";
		}
	}
}
